/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>

#include "Tracer.h"
#include "FileUtil.h"
#include "ProcUtil.h"
#include "StrUtil.h"
#include "Socket.h"

#define  MAX_PRINT_LEN  		4096    /* 打印长度最大为4096Bytes,超出后显示省略号 */

using namespace std;

namespace libemb{
Tracer::Tracer():
m_traceLevel(TRACE_LEVEL_INFO),
m_traceEnble(true),
m_traceFile(NULL),
m_remoteLevel(TRACE_LEVEL_REL)
{
	m_printBuf = new char [MAX_PRINT_LEN];
}

Tracer::~Tracer()
{
	DEL_OBJ(m_remoteSocket);
	delete [] m_printBuf;
}

/**
 *  \brief  调试信息打印
 *  \param  printLevel 打印级别
 *  \param  format 格式化字串
 *  \return void
 *  \note   none
 */
void Tracer::print(int level,const char* format,...)
{
	if ((!m_traceEnble) ||					/* 打印未使能 */
		(m_traceLevel > level) ||			/* 当前级别大于要打印级别 */
		(m_traceLevel > TRACE_LEVEL_REL))	/* 当前级别大于或等于最高可打印级别 */
	{
 		return;
	}
#if 1
	//char buf[MAX_PRINT_LEN]={0};
	char* buf = m_printBuf;
    va_list argp;
    va_start(argp,format);
	int size = MAX_PRINT_LEN-1;
    size = vsnprintf(buf,size,format,argp);
	va_end(argp);
    if (size>0) 
    {
        if (size>=MAX_PRINT_LEN) /* 超过长度了 */
        {
            buf[MAX_PRINT_LEN-5]='.';
            buf[MAX_PRINT_LEN-4]='.';
            buf[MAX_PRINT_LEN-3]='.';
            buf[MAX_PRINT_LEN-2]='\n';
            buf[MAX_PRINT_LEN-1]=0;
            size = MAX_PRINT_LEN;
        }
        write(fileno(stdout), buf, size);
		if (m_traceFile!=NULL && m_traceLevel>TRACE_LEVEL_DBG)
		{
			m_traceFile->writeData(buf,size,100000);
		}
		fflush(stdout);
    }
#else
	#if 1	/* 限制打印长度:MAX_PRINT_LEN */
    char* buf = NULL;
	int size =0;
    va_list argp;
    va_start(argp,format);
    size = vsnprintf(buf,size,format,argp);
	va_end(argp);
	if (size<0)
	{
		return;
	}
	buf = m_printBuf;
	if (size>=MAX_PRINT_LEN) 
    {
        buf[MAX_PRINT_LEN-5]='.';
        buf[MAX_PRINT_LEN-4]='.';
        buf[MAX_PRINT_LEN-3]='.';
        buf[MAX_PRINT_LEN-2]='\n';
        buf[MAX_PRINT_LEN-1]=0;
        size = MAX_PRINT_LEN;
    }
   	va_start(argp, format);
	size = vsnprintf(buf, size, format, argp);
	if (size < 0) 
	{
	   return;
	}
	va_end(argp);
	write(fileno(stdout), buf, size);
	if (m_traceFile!=NULL && m_traceLevel>TRACE_LEVEL_DBG)
	{
		m_traceFile->writeData(buf,size,100000);
	}
	fflush(stdout);
	#else
	char *buf = NULL;
	int size = 0;
	va_list argp;
	va_start(argp, format);
	size = vsnprintf(buf, size, format, argp);
	va_end(argp);
	if (size < 0)
	{
		return;
	}
	size++;
	buf = (char*)malloc(size);
	if (buf==NULL)
	{
		return;
	}
	va_start(argp, format);
	size = vsnprintf(buf, size, format, argp);
	if (size < 0) 
	{
	   free(buf);
	   return;
	}
	va_end(argp);
	write(fileno(stdout), buf, size);
	if (m_traceFile!=NULL && m_traceLevel>TRACE_LEVEL_DBG)
	{
		m_traceFile->writeData(buf,size,100000);
	}
	fflush(stdout);
	free(buf);
	#endif
#endif
}

/**
 *  \brief  调低打印级别,打印更多信息
 *  \param  void
 *  \return void
 *  \note   none
 */
void Tracer::printMore()
{
	if (m_traceEnble &&
  	 	(m_traceLevel > TRACE_LEVEL_DBG))
  	{
		m_traceLevel--;
	}
  	printf("Trace Level[%d]-enable=%d\n",m_traceLevel,m_traceEnble);
}

/**
 *  \brief  调高打印级别,打印更少信息
 *  \param  void
 *  \return void
 *  \note   none
 */
void Tracer::printLess()
{
	if (m_traceEnble &&
		(m_traceLevel <= TRACE_LEVEL_REL))
    {
    	m_traceLevel++;
	}
  	printf("Trace Level[%d]-enable=%d\n",m_traceLevel,m_traceEnble);
}

/**
 *  \brief  设置打印级别
 *  \param  level 打印级别
 *  \return void
 *  \note   默认打印级别为TRACE_LEVEL_INFO(不打印DBG信息)
 */
bool Tracer::setLevel(int level)
{
    if (level<0 || level>TRACE_LEVEL_REL)
    {
        return false;
    }
	m_traceLevel=level;
	return true;
}

/**
 *  \brief  返回当前打印级别
 *  \param  void
 *  \return it 当前打印级别
 *  \note   none
 */
int Tracer::getLevel()
{
	return m_traceLevel;
}

/**
 *  \brief  打印调试使能
 *  \param  enable 是否使能
 *  \return void
 *  \note   默认使能
 */
void Tracer::setEnable(bool enable)
{
	m_traceEnble=enable;
}


bool Tracer::fileTracerInit(const char* fileName)
{
	if (fileName==NULL)
	{
		return false;
	}
	if (m_traceFile!=NULL)
	{
		m_traceFile->close();
		DEL_OBJ(m_traceFile);
	}
	m_traceFile = NEW_OBJ File;
	if(!m_traceFile->open(fileName, IO_MODE_REWR_ORNEW))
	{
		DEL_OBJ(m_traceFile);
		return false;
	}
	return true;
}

void Tracer::fileTracerQuit()
{
	if (m_traceFile!=NULL)
	{
		m_traceFile->close();
		DEL_OBJ(m_traceFile);
	}
}

/**
 *  \brief  以16进制字串的格式打印数据
 *  \param  level 打印级别
 *  \param  tag 标识字串
 *  \param  buf 要打印的内存块地址
 *  \param  len 要打印的内存块长度
 *  \return void
 *  \note   none
 */
void Tracer::printHex(int level,const char* tag,const char* buf,int len)
{
	if ((!m_traceEnble) ||
		(level < m_traceLevel) ||
        (NULL==tag &&
        NULL==buf) ||
		len <=0)
    {
     	return;
    }
	printf("%s(%02d)-[",tag,len);
	for(int i=0;i<len;i++)
	{
		printf("%02x ",(unsigned char)(*(buf+i)));
	}
	printf("]\n");
}

bool Tracer::remoteTracerInit(uint16 port)
{
	if (m_remoteSocket!=NULL)
	{
		DEL_OBJ(m_remoteSocket);
	}
	m_remoteSocket = NEW_OBJ UdpSocket();
	if (!m_remoteSocket->open("127.0.0.1", port))
	{
		DEL_OBJ(m_remoteSocket);
		return false;
	}
	m_remoteLevel = m_traceLevel;
	return true;
}
bool Tracer::remoteTracerWait(int usTimeout)
{
	if (m_remoteSocket==NULL)
	{
		return false;
	}
	char buf[64] = {0};
	if (m_remoteSocket->readData(buf, sizeof(buf),usTimeout)>0)
	{
		/* 协议文本“1:4:/dev/pts/x” */
		std::string msg(buf);
        if (msg.size()>=14) 
        {
            if (msg.substr(3,10)==":/dev/pts/") 
            {
                bool enable = !!atoi(msg.substr(0,1).c_str());
				int level = atoi(msg.substr(2,1).c_str());
                std::string ptsDev = msg.substr(4);
                if (enable) 
				{
					setLevel(level);
                    close(1);
				    close(2);
				    /* 在新终端打开标准输出 */
				    int ret = (int)open(ptsDev.c_str(), 1);
				    if(ret<0)
				    {
				         TRACE_ERR_CLASS("redirect stdout to %s error:%s.\n",ptsDev.c_str(), ERROR_STRING);
				         return false;    
				    }
				    /* 在新终端打开标准错误输出 */
				    ret = (int)open(ptsDev.c_str(), 2);
				    if(ret<0)
				    {
				        TRACE_ERR_CLASS("redirect stderr to %s error:%s.\n", ptsDev.c_str(), ERROR_STRING); 
				        return false;
				    }
				    fflush(NULL);
					TRACE_REL_CLASS("ctrace setup, level:%d\n",level);
				    return true;
                } 
				else 
				{
					setLevel(m_remoteLevel);
                    std::string ttyName = ProcUtil::execute("tty");
				    if (ttyName.empty()) {
				        return false;
				    }
				    ttyName = StrUtil::trimEndingBlank(ttyName);
				    /* 在当前终端重新打开标准输出和标准错误输出 */
				    freopen(ttyName.c_str(),"w",stdout);
				    freopen(ttyName.c_str(),"w",stderr);
				    /* 不知道为什么会产生这样一个文件!只能强制把它删除! */
				    ProcUtil::execute("rm -rf not\\ a\\ tty ");
					TRACE_REL_CLASS("ctrace quit, level:%d\n",m_remoteLevel);
				    return true;
                }
            }
        }
	}
}
void Tracer::remoteTracerQuit()
{
	if (m_remoteSocket!=NULL)
	{
		DEL_OBJ(m_remoteSocket);
	}
}
}